﻿//Copyright (c) 2010 Christopher E. S. King (http://chriskingconsulting.com)
//
//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 
// (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do 
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using System.Reflection;
using System.Collections;
using System.IO;
using System.Diagnostics.Contracts;

using King.Extensions;
using King.Text;
using King.Reflection;
using King.Language;
using King.Antlr3;

using Antlr.Runtime;
using Antlr.Runtime.Tree;

namespace King.Language.CSharp {

    internal partial class AntlrCSharpPreProcessorParser {

        internal class ConditionalScopeFrame {
            private AntlrCSharpPreProcessorParser m_parser;
            private ConditionalScopeFrame m_parentScope;
            private bool m_skipSection;
            private bool m_caseTaken;
            private bool m_inElseCase;

            internal ConditionalScopeFrame(AntlrCSharpPreProcessorParser parser) {
                m_parser = parser;
                m_parentScope = parser.ConditionalScope;
            }

            internal ConditionalScopeFrame Parent {
                get { return m_parentScope; }
            }
            internal bool SkipSection {
                get { return m_skipSection; }
                set {
                    if (value != SkipSection)
                        m_parser.OnSkipSection(value);

                    m_skipSection = value;

                    m_parser.Lexer.EnableInactiveSection = m_skipSection;
                }
            }
            internal bool CaseTaken {
                get { return m_caseTaken; }
                set {
                    if (value != CaseTaken)
                        m_parser.OnCaseTaken(value);

                    m_caseTaken = value;
                }
            }
            internal bool InElseCase {
                get { return m_inElseCase; }
                set {
                    if (value != true)
                        throw new InvalidOperationException();

                    if (value != InElseCase)
                        m_parser.OnInElseCase(value);

                    m_inElseCase = value;
                }
            }
        }

        private bool m_inPreProcessorLine;
        private Stack<ConditionalScopeFrame> m_stack;
        private HashSet<string> m_ppSymbols;

        partial void OnSkipSection(bool value);
        partial void OnCaseTaken(bool value);
        partial void OnInElseCase(bool value);
        partial void OnEnterConditional();
        partial void OnExitConditional();

        partial void OnCreated() {
            m_ppSymbols = new HashSet<string>(System.StringComparer.InvariantCultureIgnoreCase);
            m_stack = new Stack<ConditionalScopeFrame>();
        }

        private AntlrCSharpPreProcessorLexer Lexer { 
            get { return (AntlrCSharpPreProcessorLexer)TokenStream.TokenSource; } 
        }
        
        private void Define(NillableToken token) {
            m_ppSymbols.Add(token.Text);
        }
        private void Undefine(NillableToken token) {
            m_ppSymbols.Remove(token.Text);
        }
        private bool IsDefined(NillableToken token) {
            return m_ppSymbols.Contains(token.Text);
        }
        private bool ParseBool(NillableToken token) {
            return bool.Parse(token.Text);
        }

        private void EnterConditionalScope() {
            OnEnterConditional();
            m_stack.Push(new ConditionalScopeFrame(this));
        }
        private void ExitConditionalScope() {
            OnExitConditional();
            m_stack.Pop();
        }
        private ConditionalScopeFrame ConditionalScope {
            get { return m_stack.Count == 0 ? null : m_stack.Peek(); }
        }

        private bool InPreProcessorLine {
            get { return m_inPreProcessorLine; }
            set { 
                m_inPreProcessorLine = value;

                var skipSection = ConditionalScope != null && ConditionalScope.SkipSection;
                Lexer.EnableNewLine = InPreProcessorLine || !skipSection;
                Lexer.EnableInactiveSection = !InPreProcessorLine && skipSection;
            }
        }

        partial void OnSkipSection(bool value) {
            Console.WriteLine("SkipSection = " + value);
        }

        protected override object RecoverFromMismatchedToken(IIntStream input, int ttype, BitSet follow) {
            throw new MismatchedTokenException(ttype, input, TokenNames);
        }
        public override void Recover(IIntStream input, RecognitionException re) {
            throw re;
        }

        internal void AddPreProcessorSymbols(IEnumerable<string> symbols) {
            if (symbols == null)
                return;
            foreach (var symbol in symbols) m_ppSymbols.Add(symbol);
        }
        internal void Yield() {
            Lexer.Yield();
        }
        internal void Continue() {
            if (Lexer.Continue())
                input.Consume();
        }

        private int m_indent;
        partial void EnterRule(string ruleName, int ruleIndex) {
            Console.WriteLine("".PadLeft(m_indent++) + "Enter " + ruleName + " " + ruleIndex);
        }
        partial void LeaveRule(string ruleName, int ruleIndex) {
            Console.WriteLine("".PadLeft(--m_indent) + "Leave " + ruleName + " " + ruleIndex);
        }
        private void ExpressionResult(bool value) {
            Console.WriteLine("Expression = " + value);
        }
    }
    internal partial class AntlrCSharpPreProcessorLexer : IMyYieldableTokenSource {

        private TokenTreeAdaptor m_tokenTreeAdapter;
        private bool m_enableNewLine;
        private bool m_enableInactiveSection;
        private bool m_yield;

        partial void OnCreated() {
        }
        
        public override void Recover(IIntStream input, RecognitionException re) {
            throw re;
        }
        public override void Recover(RecognitionException re) {
            throw re;
        }
        
        protected override object RecoverFromMismatchedToken(IIntStream input, int ttype, BitSet follow) {
            throw new MismatchedTokenException(ttype, input, TokenNames);
        }
        protected override void ParseNextToken() {
            if (m_yield) {
                Emit(m_tokenTreeAdapter.Create(YIELD, null, state.channel, 0, 0, 0, 0));

            } else if (input.LA(1) == MyCharStreamConstants.EndOfFile) {
                Emit(m_tokenTreeAdapter.Create(MyTokenTypes.EndOfFile, null, state.channel, 0, 0, 0, 0));

            } else {
                mTokens();
            }
        }

        internal bool EnableInactiveSection {
            get { return m_enableInactiveSection; }
            set { m_enableInactiveSection = value; }
        }
        internal bool EnableNewLine {
            get { return m_enableNewLine; }
            set { m_enableNewLine = value; }
        }
        internal TokenTreeAdaptor TokenTreeAdapter {
            get { return m_tokenTreeAdapter; }
            set { m_tokenTreeAdapter = value; }
        }

        internal void Yield() {
            m_yield = true;
        }
        internal bool Continue() {
            var result = m_yield;
            m_yield = false;
            return result;
        }

        public override NillableToken Emit() {
            var token = m_tokenTreeAdapter.Create(
                state.type, 
                state.text, 
                state.channel, 
                state.tokenStartCharIndex,
                CharIndex - state.tokenStartCharIndex,
                state.tokenStartLine, 
                state.tokenStartCharPositionInLine);

            Emit(token);

            if (!state.skip) {
                var text = state.text;
                if (state.type == NEW_LINE)
                    text = @"\r\n";
                Console.WriteLine(text + " " + token.ToString());
            }
            return token;
        }

        bool IMyYieldableTokenSource.Yield {
            get { return m_yield && state.token.Value.Type == YIELD; }
        }
    }
}
